package com.ruoyi.plus.utils;

import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.enums.HttpMethod;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.plus.controller.po.MultipartFileChunk;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;

@Aspect
@Component
@Slf4j
public class LogControllerAspect {

    @Pointcut("within(com.ruoyi..*) && @within(org.springframework.web.bind.annotation.RestController)")
    public void printParams() {

    }

    /**
     * 打印请求参数
     *
     * @param joinPoint
     */
    @Before(value = "printParams()")
    public void paramsLog(JoinPoint joinPoint) {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        String requestMethod = request.getMethod();
        String params = "";
        if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) {
            params = argsArrayToString(joinPoint.getArgs());
        } else {
            Map<String, String[]> paramsMap = request.getParameterMap();
            params = JSON.toJSONString(paramsMap);
        }
        log.debug("{} {} {} {} {}", IpUtils.getIpAddr(request), request.getMethod(), request.getRequestURL(), joinPoint.getSignature(), params);
    }

    /**
     * 打印响应内容
     *
     * @param o
     */
    @AfterReturning(returning = "o", pointcut = "printParams()")
    public void methodAfterReturning(Object o) {
        //log.debug(JSON.toJSONString(o));
    }

    @AfterThrowing(value = "printParams()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        String requestMethod = request.getMethod();
        String params = "";
        if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) {
            params = argsArrayToString(joinPoint.getArgs());
        } else {
            Map<String, String[]> paramsMap = request.getParameterMap();
            params = JSON.toJSONString(paramsMap);
        }
        log.error("{} {} {} {} {}", IpUtils.getIpAddr(request), request.getMethod(), request.getRequestURL(), joinPoint.getSignature(), params, e);
    }


    /**
     * 打印方法耗时
     *
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("printParams()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        //long startTime = System.currentTimeMillis();
        //Object result = joinPoint.proceed();
        //log.debug("执行方法：{}，用时:{} 毫秒 , ", joinPoint.getSignature().getName(), System.currentTimeMillis() - startTime);
        //return result;
        return joinPoint.proceed();
    }

    /**
     * 参数拼装
     */
    private String argsArrayToString(Object[] paramsArray) {
        String params = "";
        if (paramsArray != null && paramsArray.length > 0) {
            for (int i = 0; i < paramsArray.length; i++) {
                if (StringUtils.isNotNull(paramsArray[i]) && !isFilterObject(paramsArray[i])) {
                    Object jsonObj = JSON.toJSON(paramsArray[i]);
                    if (jsonObj != null) {
                        params += jsonObj.toString() + " ";
                    }
                }
            }
        }
        return params.trim();
    }

    /**
     * 判断是否需要过滤的对象。
     *
     * @param o 对象信息。
     * @return 如果是需要过滤的对象，则返回true；否则返回false。
     */
    @SuppressWarnings("rawtypes")
    public boolean isFilterObject(final Object o) {
        Class<?> clazz = o.getClass();
        if (clazz.isArray()) {
            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
        } else if (Collection.class.isAssignableFrom(clazz)) {
            Collection collection = (Collection) o;
            for (Iterator iter = collection.iterator(); iter.hasNext(); ) {
                return iter.next() instanceof MultipartFile;
            }
        } else if (Map.class.isAssignableFrom(clazz)) {
            Map map = (Map) o;
            for (Iterator iter = map.entrySet().iterator(); iter.hasNext(); ) {
                Map.Entry entry = (Map.Entry) iter.next();
                return entry.getValue() instanceof MultipartFile;
            }
        }
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse || o instanceof BindingResult
                ||o instanceof MultipartFileChunk;
    }


}